from GUI import GUI
import wx
import os
from ConfigfileReader import ConfigfileReader
from PreferencesFileReader import PreferencesFileReader
from SensorState import SensorState
from BLState import BLState
from GHJState import GHJState
from VisualizationState import VisualizationState
from DriverInterface import DriverInterface
from FoBDriver import FoBDriver
from numpy import size
from PreferencesDialog import PreferencesDialog
from AboutDialog import AboutDialog
from AddBonyLandmark import AddBonyLandmark
from Sensor import Sensor
from ComportCheck import ComportCheck
import BaseCalc

import replacebone


##  The main class to run the program.
class Main(wx.App):        

    try:
        import MotionVA.AngleCalculations as AngleCalculations
        motion_analyser_available = True        
    except ImportError:
        motion_analyser_available = False
        pass
    motion_analysis_started = False
    

    ##  Initializes the GUI, resets the system.
    #   @return[int]    1 if initialization is successful.
    def OnInit(self):
        self.replacement_tuples = []
        
        wx.InitAllImageHandlers()
        self.baseCalc = BaseCalc.create(self, self)

        splash = wx.SplashScreen(wx.Bitmap("images/Splash.png"),
                 wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT,
                 1000, None, style=wx.STAY_ON_TOP)
        ##  Indicates whether OnNew has been used yet.
        self.FIRST_TIME = True
        ##  Set to true when the FoB system has successfully started.
        self.FOB_RUNNING = False
        ## Indicates whether data is written to a file.
        self.DATA_WRITE = True
        ## Indicates whether fast data acquisition is used or not
        self.FAST_ACQUISITION = False

        ##  Instantiation of the GUI class.
        self.gui = GUI(self, None, -1, "")
        self.SetTopWindow(self.gui)
        self.Reset()
        self.OnNew(self)
        self.gui.Show()

        return 1

    ##  Resets all attributes.
    def Reset(self):
        ##  Instantiation of the PreferencesFileReader which contains references to all the three preference files.
        self.prefReader = PreferencesFileReader(self.gui)
        ##  Instantiation of the ConfigfileReader which parses the configuration file.
        self.config = ConfigfileReader(self.gui,self.prefReader.configFile)
        ##  The GSI mapping from fob0_x to COM[port]_[address] as read from the config.ini file.
        self.coms = self.config.ReadComports()
        ##  A matrix representation of the calibration file.
        self.calibrationMatrix = self.prefReader.ReadCalibration(self.prefReader.calibrationFile)
        ##  The list containing the bony landmarks.
        self.bonyLandmarks = self.config.ReadBonyLandmarks()
        ##  A dictionary containing the sensor and corresponding coordinates on the body.
        self.sensorPictPos = self.config.ReadSensorPictPos()
        ##  A dictionary containing the number and names of GH-Joints.
        self.GHJoints = self.config.ReadGHJoints(False)
        ##  Indicates whether 3D smoothing of the model is activated or not.
        self.Smooth3DModel = self.config.Read3DSmoothing()
        
        #~ self.InitiateFOBConnection()        
        #~ self.Reset()
        #~ self.gui.LoadNotebook()
        #~ self.states.visualization.ToggleSensors()
        #~ self.states.visualization.ToggleBonyLandmarks()
        #~ self.gui.notebook.ChangeSelection(0)
        #~ self.driver = FoBDriver(self,10,False)

        ##  A (key,value) mapping from sensor name to position, e.g. ('fob0_3', 'Thorax').
        dp_dict = self.config.ReadDefaultPositions()
        ##  The list of Sensor objects taken from dp_dict.
        self.sensors = [0]
        for gsi, position in dp_dict.items():
            if position == "Pointer":
                self.sensors[0] = Sensor(gsi, position)
            else:
                self.sensors.append(Sensor(gsi, position))

        ## Object containing all the four states in the program.
        #  Create 'States' class on the fly.
        self.states = type('States', (object,), {})
        ##  The Sensorstate.
        self.states.sensors = SensorState(self) # Sensors, tab 0
        ##  The BLState.
        self.states.bony_landmark = BLState(self) # Bony Landmarks, tab 1
        ##  The GHJState.
        self.states.gh_cor = GHJState(self) # GH-Joint, tab 2
        ##  The VisualizationState.
        self.states.visualization = VisualizationState(self) # Visualization, tab 3

    def InitiateFOBConnection(self):
        ## Error checking to see whether or not the fob hardware can be run
        try:
            ##  The current hardware driver used in the system.
            self.driver = FoBDriver(self)
            self.FOB_RUNNING = True
        except RuntimeError:
            #~ msg = "Could not open a Fob hardware connection. Only the playback panel will be available for use."
            #~ dlg = wx.MessageDialog(self.gui, msg, 'Warning', wx.OK | wx.NO_DEFAULT | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP)
            #~ dlg.ShowModal()
            self.driver = FoBDriver(self,10,False)

    ##  Starts a new measurement session.
    def OnNew(self, event):

        ## Makes sure the visualization loop is stopped, otherwise errors will occur once the FOB has been reset
        if not self.FIRST_TIME: 
            self.states.visualization.Stop()
            try:
                self.driver.close()
                self.InitiateFOBConnection()
            except AttributeError:
                print "No tracking system connected (yet)."
                print "Connecting..."
                self.InitiateFOBConnection()
        else:
            self.driver = FoBDriver(self,10,False)
        #~ else:
            #~ self.InitiateFOBConnection()

        self.FIRST_TIME = False

        self.Reset()
        self.gui.LoadNotebook()

        self.states.visualization.ToggleSensors()
        self.states.visualization.ToggleBonyLandmarks()

        self.gui.notebook.ChangeSelection(0)
        self.replacement_tuples = []

    ##  Opens the preferences dialog window.
    def OnPreferences(self, event):
        dlg = PreferencesDialog(self.gui)
        dlg.CenterOnScreen()
        
        #New preferences are now saved in the preferences.ini file.
        if dlg.ShowModal() == wx.ID_OK:
            self.prefReader.SavePreferences(dlg)

            #Dialog asking user to reset the system once the preferences have been saved.
            msg = "The preferences have been saved.\n\n For these changes to take effect the Flock Of Birds Visualizer needs to reset.\n By resetting, all current measurement data and imported recordings will be lost.\n\n Do you want to reset the Flock Of Birds Visualizer?"
            dlg2 = wx.MessageDialog(self.gui, msg, 'Warning', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_EXCLAMATION)
            if(dlg2.ShowModal() == wx.ID_YES):
                self.OnNew(self)
            dlg2.Destroy()

        dlg.Destroy()
        
    def OnStoreAbsMatrices(self, event):
        self.states.visualization.playbackState.PlayAndStore()

    ##  Opens the about dialog window.
    def OnAbout(self, event):
        dlg = AboutDialog(self.gui)
        dlg.ShowModal()
        dlg.CenterOnScreen()
        dlg.Destroy()

    ##  Toggles the sensor spheres in the visualization on or off.
    def OnToggleSensors(self, event):
        self.states.visualization.ToggleSensors()

    ##  Toggles the bony landmark spheres in the visualization on or off.
    def OnToggleBonyLandmarks(self, event):
        self.states.visualization.ToggleBonyLandmarks()

    ##  Toggles the 3D model in the visualization on or off.
    def OnToggle3DModel(self, event):
        self.states.visualization.Toggle3DModel()

    ##  Exits the program.
    def OnQuit(self, event):
        self.states.visualization.Stop()
        self.gui.Destroy()
        self.driver.close()

    ##  Opens the menu for adding bony landmarks.
    def OnAddBonyLandmark(self, event):
        AddBonyLandmark(self.gui.notebook,self.gui)

    ## Opens the menu for checking the COM ports.
    def OnCheckCOMports(self, event):
        #import ComportCheck
        frame = ComportCheck(None, -1, 'COM-Port Check', self.config, self.driver.fob_dict)
        frame.Show(True)
        self.SetTopWindow(frame)

    ## Enables writing raw data to a file
    def OnDataWrite(self, event):
        self.DATA_WRITE = not self.DATA_WRITE
        print self.DATA_WRITE

    ## Enables the optimal efficiency for acquiring data, disabling any calculations of visualizations. aka TurboMode
    def OnFastAcquisition(self, event):
        self.FAST_ACQUISITION = not self.FAST_ACQUISITION
        print self.FAST_ACQUISITION
        
    def OnExportBlVecs(self, event):
        dlg = wx.FileDialog(self.gui, message="Save Bony Landmark Vectors", defaultDir=os.getcwd(), defaultFile="",
                        wildcard="Txt-file (*.txt)|*.txt", style=wx.SAVE | wx.CHANGE_DIR | wx.OVERWRITE_PROMPT)
        if dlg.ShowModal() == wx.ID_OK:
            txtpath = dlg.GetPath()  
            fileWriter = open(txtpath, 'w')
                
        blRange = len(self.bonyLandmarks)
        for i in range(0, blRange):            
            a = self.bonyLandmarks[i].relativePosition#.ToList()
            fileWriter.write('%.3f  %.3f  %.3f\n' % (a[0], a[1], a[2]))
        fileWriter.close()
        
    def GetPath(self, event):
        dlg = wx.FileDialog(None, message="Select STL file", defaultDir=os.getcwd(), defaultFile="",
        wildcard="STL file (*.stl)|*.stl", style=wx.OPEN | wx.CHANGE_DIR)
        if dlg.ShowModal() == wx.ID_OK:
            self.replaceDlg.text_ctrl_1.SetValue(dlg.GetPath())
                
    def OnReplaceBone(self, event):
        self.replaceDlg = replacebone.MyDialog(None, -1, '')        
        self.replaceDlg.button_browse.Bind(wx.EVT_BUTTON, self.GetPath)
        self.replaceDlg.choice_1.Bind(wx.EVT_CHOICE, self.OnSelectionChanged)
        if self.replaceDlg.ShowModal() == wx.ID_OK:
            if self.replaceDlg.text_ctrl_1.GetValue() != "":
                #Get the index of the bone.
                #In this version, bones 0-73 are excluded to make the drop down menu easier to use.
                boneindex = self.replaceDlg.choice_1.GetCurrentSelection() + 73
                #Add the boneindex and its file path to the list of replacement tuples
                self.replacement_tuples.append([boneindex, self.replaceDlg.text_ctrl_1.GetValue()])
                # Update the __meshPos for each BL that should be replaced
                for i in range(0, self.replaceDlg.grid_1.GetNumberRows()-1):
                    #Check if the row actually contains information
                    if self.replaceDlg.grid_1.GetCellValue(i,1) != "":
                        #Get the index of the current BL
                        blIndex = int(self.replaceDlg.grid_1.GetCellValue(i,0).split('.')[0])
                        #Replace the BL
                        self.states.visualization.UpdateBL(blIndex, [float(self.replaceDlg.grid_1.GetCellValue(i,1)), float(self.replaceDlg.grid_1.GetCellValue(i,2)), float(self.replaceDlg.grid_1.GetCellValue(i,3))])
        self.replaceDlg.Destroy()
        print self.replacement_tuples
        
    #Called when a new bone is selected in the drop down menu of the bone replacement dialog
    def OnSelectionChanged(self, event):
        connections = self.config.Read3DModelConnection()
        boneindex = self.replaceDlg.choice_1.GetCurrentSelection() + 73
        #Find the correct landmarks and update the table.
        for i in range(0, len(connections)):
            if (boneindex in connections[i][0]):
                landmarks = connections[i][1]
                #Get the names of all landmarks
                names = self.config.ReadBonyLandmarkNames()
                #Append the GH joints
                names.append((24, 'Right GH'))
                names.append((25, 'Left GH'))
                #For each landmark, update the table in the dialog with the correct name and current BL values.
                for j in range(0, len(landmarks)):
                    self.replaceDlg.grid_1.SetCellValue(j, 0, str(names[landmarks[j]][0]) + '. ' +  names[landmarks[j]][1])
                    self.replaceDlg.grid_1.SetCellValue(j, 1, str(self.states.visualization.GetBL(landmarks[j])[0]))
                    self.replaceDlg.grid_1.SetCellValue(j, 2, str(self.states.visualization.GetBL(landmarks[j])[1]))
                    self.replaceDlg.grid_1.SetCellValue(j, 3, str(self.states.visualization.GetBL(landmarks[j])[2]))
                #Clear all other cells
                for k in range(j+1, self.replaceDlg.grid_1.GetNumberRows()-1):
                    self.replaceDlg.grid_1.SetCellValue(k, 0, '')
                    self.replaceDlg.grid_1.SetCellValue(k, 1, '')
                    self.replaceDlg.grid_1.SetCellValue(k, 2, '')
                    self.replaceDlg.grid_1.SetCellValue(k, 3, '')
        
    def OnStartAnalyser(self, event):
        if self.motion_analyser_available:
            self.angleCalculations = self.AngleCalculations.create(self, self)
            self.angleCalculations.CreatePatternViewer()
            self.motion_analysis_started = True
        else:
            msg = "It seems that the Motion Analysis software is not installed. Please check your installation directory."
            dlg = wx.MessageDialog(self.gui, msg, 'Warning', wx.OK | wx.NO_DEFAULT | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP)
            dlg.ShowModal()

    ## Returns a dictionary containing the the indices of the bony landmarks in as the key, and their ID as item.
    #  @param[in]       withGH  A boolean value that is set to return a dictionary including or exclude GHJoint.
    #  @return[dict]    Dictionary of bony landmarks indices and ID's.
    def GetBonyLandmarkDict(self,withGH):
        bl_dict = {}
        blRange = len(self.bonyLandmarks)
        if not withGH: blRange -= len(self.GHJoints)
        for i in range(0, blRange):
            bl_dict[self.bonyLandmarks[i].ID] = i
        return bl_dict

if __name__ == "__main__":
    app = Main(0)
    app.MainLoop()